I have ported my old project “pNesX” game console emulator to the nucleo.

Dependencies:   SDFileSystem mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers pNesX_Apu.cpp Source File

pNesX_Apu.cpp

00001 /*===================================================================*/
00002 /*                                                                   */
00003 /*  pNesX_Apu.cpp : NES APU emulation (for Nucleo)                   */
00004 /*                                                                   */
00005 /*  2016/1/20  Racoon                                                */
00006 /*                                                                   */
00007 /*===================================================================*/
00008 
00009 #include "mbed.h"
00010 #include "SDFileSystem.h"
00011 
00012 #include "pNesX.h"
00013 #include "pNesX_System.h"
00014 
00015 //====================
00016 // DAC
00017 //====================
00018 AnalogOut DAC_Out(PA_4);
00019 WORD TrDac, P1Dac, P2Dac, NsDac, MixDac;
00020 #define DACFreq 24000
00021 // Pulse clock(8 steps) = 1789773 / 2 / DACFreq * 1000
00022 #define PuClock 37287
00023 // Triangle clock(32 steps) = 1789773 / DACFreq * 1000
00024 #define TrClock 74574
00025 
00026 //====================
00027 // DAC Timer
00028 //====================
00029 #define TIMx                TIM4
00030 #define TIMx_CLK_ENABLE     __HAL_RCC_TIM4_CLK_ENABLE
00031 #define TIMx_IRQn           TIM4_IRQn
00032 #define TIMx_IRQHandler     TIM4_IRQHandler
00033 TIM_HandleTypeDef TimHandle;
00034 
00035 //====================
00036 // Audio channel variables
00037 //====================
00038 
00039 const BYTE LengthTable[] = {10, 254, 20,  2, 40,  4, 80,  6, 160,  8, 60, 10, 14, 12, 26, 14,
00040                             12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30};
00041 
00042 // Minimum Volume
00043 #define MV 4096
00044 const WORD Pulse_Table[4][8] = { 0,MV, 0, 0, 0, 0, 0, 0,
00045                                  0,MV,MV, 0, 0, 0, 0, 0,
00046                                  0,MV,MV,MV,MV, 0, 0, 0,
00047                                 MV, 0, 0,MV,MV,MV,MV,MV};
00048 // Pulse1
00049 bool P1Enable;
00050 int P1Timer;
00051 int P1WaveLength;
00052 int P1WavePeriod;
00053 int P1Lapse;
00054 int P1Duty;
00055 int P1Vol;
00056 int P1LengthCounter;
00057 int P1EnvSeq;
00058 int P1EnvCnt;
00059 int P1Sweep;
00060 int P1SwCnt;
00061 int P1SwLevel;
00062 bool P1SwReloadFlag;
00063 bool P1SwOverflow;
00064 
00065 // Pulse2
00066 bool P2Enable;
00067 int P2Timer;
00068 int P2WaveLength;
00069 int P2WavePeriod;
00070 int P2Lapse;
00071 int P2Duty;
00072 int P2Vol;
00073 int P2LengthCounter;
00074 int P2EnvSeq;
00075 int P2EnvCnt;
00076 int P2Sweep;
00077 int P2SwCnt;
00078 int P2SwLevel;
00079 bool P2SwReloadFlag;
00080 bool P2SwOverflow;
00081     
00082 // Triangle                   
00083 bool TrEnable;
00084 int TrTimer;
00085 const WORD Triangle_Table[] = {61440,57344,53248,49152,45056,40960,36864,32768,28672,24576,20480,16384,12288,8192,4096,0,
00086                                0,4096,8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248,57344,61440};
00087 bool LinearCounterReloadFlag;
00088 int LinearCounter;
00089 int TrLengthCounter;
00090 int TrWaveLength;
00091 int TrWavePeriod;
00092 int TrLapse;
00093 
00094 // Noise
00095 bool NsEnable;
00096 const WORD Noise_Freq[] = {0,0,0,0,0,0,12000,11186,8860,7046,4710,3523,2349,1762,880,440};
00097 int NsFreq;
00098 int NsSum;
00099 int NsVol;
00100 int NsLengthCounter;
00101 int NsEnvSeq;
00102 int NsEnvCnt;
00103 
00104 // DMC
00105 int DmOutLevel;
00106 
00107 /*-------------------------------------------------------------------*/
00108 /*  DAC Timer callback                                               */
00109 /*-------------------------------------------------------------------*/
00110 extern "C" {
00111 void TIMx_IRQHandler(void)
00112 {
00113   HAL_TIM_IRQHandler(&TimHandle);
00114 }
00115 
00116 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
00117 {
00118   TIMx_CLK_ENABLE();
00119   
00120   HAL_NVIC_SetPriority(TIMx_IRQn, 4, 0);
00121   HAL_NVIC_EnableIRQ(TIMx_IRQn);
00122 }
00123 
00124 static void Error_Handler(void)
00125 {
00126   while(1);
00127 }
00128 } // extern "C"
00129 
00130 /*-------------------------------------------------------------------*/
00131 /*  Randomize value (Noise channel)                                  */
00132 /*-------------------------------------------------------------------*/
00133 WORD ShiftReg = 1;
00134 
00135 WORD Rand96()
00136 {
00137     ShiftReg |= ((ShiftReg ^ (ShiftReg >> 6)) & 1) << 15;
00138     ShiftReg >>= 1;
00139     return (ShiftReg & 1) ? MV : 0;    
00140 }
00141 
00142 WORD Rand32k()
00143 {
00144     ShiftReg |= ((ShiftReg ^ (ShiftReg >> 1)) & 1) << 15;
00145     ShiftReg >>= 1;
00146     return (ShiftReg & 1) ? MV : 0;    
00147 }
00148 
00149 /*-------------------------------------------------------------------*/
00150 /*  DAC Timer Interrup                                               */
00151 /*-------------------------------------------------------------------*/
00152 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
00153 {
00154     // Pulse1
00155     if (P1Enable && P1LengthCounter > 0 && !P1SwOverflow && P1Timer > 8)
00156     {
00157         P1Lapse += PuClock; 
00158         P1Lapse %= P1WavePeriod;
00159         P1Dac = Pulse_Table[P1Duty][P1Lapse / P1WaveLength] * P1Vol;           
00160     }
00161 
00162     // Pulse2
00163     if (P2Enable && P2LengthCounter > 0 && !P2SwOverflow && P2Timer > 8)
00164     {
00165         P2Lapse += PuClock; 
00166         P2Lapse %= P2WavePeriod;
00167         P2Dac = Pulse_Table[P2Duty][P2Lapse / P2WaveLength] * P2Vol;   
00168     }
00169     
00170     // Triangle
00171     if (TrEnable && LinearCounter > 0 && TrLengthCounter > 0)
00172     {
00173         TrLapse += TrClock; 
00174         TrLapse %= TrWavePeriod;
00175         TrDac = Triangle_Table[TrLapse / TrWaveLength];        
00176         TrDac -= (TrDac * DmOutLevel / 295);
00177     }
00178     
00179     // Noise
00180     if (NsEnable && NsLengthCounter > 0)
00181     {
00182         NsSum += NsFreq;
00183         if (NsSum >= DACFreq)
00184         {
00185             NsSum %= DACFreq;
00186 
00187             NsDac = ((APU_Reg[0xe] & 0x80) ? Rand96() : Rand32k()) * NsVol;
00188             NsDac -= (NsDac * DmOutLevel / 295);            
00189         }        
00190     }
00191         
00192     // Mixing
00193     MixDac = (P1Dac + P2Dac + TrDac + NsDac) / 4;
00194         
00195     // Output to DAC
00196     DAC_Out.write_u16(MixDac);   
00197 }
00198  
00199 /*-------------------------------------------------------------------*/
00200 /*  Apu Initialize Function                                          */
00201 /*-------------------------------------------------------------------*/
00202 void ApuInit()
00203 {
00204     // Setup DAC Timer  
00205     TimHandle.Instance = TIMx;   
00206     TimHandle.Init.Prescaler = (uint32_t) ((SystemCoreClock / 2) / 240000) - 1; // 240000Hz
00207     TimHandle.Init.Period = 10 - 1; // 240000/10=24000Hz 
00208     TimHandle.Init.ClockDivision = 0;
00209     TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
00210     
00211     if(HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
00212         Error_Handler();
00213     
00214     if(HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)
00215         Error_Handler();
00216 }
00217 
00218 /*-------------------------------------------------------------------*/
00219 /*  Apu Mute                                                         */
00220 /*-------------------------------------------------------------------*/
00221 void ApuMute(bool mute)
00222 {
00223     if (mute)
00224     {
00225         if(HAL_TIM_Base_Stop_IT(&TimHandle) != HAL_OK)
00226             Error_Handler();
00227     }
00228     else
00229     {
00230         if(HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)
00231             Error_Handler();
00232     }    
00233 }
00234       
00235 /*-------------------------------------------------------------------*/
00236 /*  Apu Write Function                                               */
00237 /*-------------------------------------------------------------------*/
00238 void ApuWrite( WORD wAddr, BYTE byData )
00239 {
00240   APU_Reg[ wAddr ] = byData;
00241 
00242   switch( wAddr )
00243   {
00244     //====================
00245     // Pulse1
00246     //====================
00247     case 0:
00248       P1Duty = (byData & 0xc0) >> 6;
00249     
00250       if (byData & 0x10)
00251       {
00252         // constant volume
00253         P1Vol = byData & 0xf;
00254       }
00255       else
00256       {
00257         // envelope on
00258         P1Vol = 15;
00259         P1EnvCnt = (byData & 0xf) + 1;
00260       }
00261       break;
00262 
00263     case 1:
00264       P1SwReloadFlag = true;
00265       P1SwLevel = (byData & 7);
00266       break;
00267   
00268     case 2:
00269     case 3:
00270       P1Timer = APU_Reg[3] & 7;
00271       P1Timer = (P1Timer << 8) | APU_Reg[2];
00272       P1WaveLength = (P1Timer+1) * 1000;
00273       P1WavePeriod = P1WaveLength * 8;               
00274 
00275       if (wAddr == 3)
00276       {
00277         P1LengthCounter = LengthTable[(byData & 0xf8) >> 3];
00278     
00279         // Reset sequencer
00280         P1Lapse = 0;
00281         P1EnvSeq = P1EnvCnt;
00282         P1Sweep = 0;
00283         P1SwCnt = 0;
00284         P1SwOverflow = false;
00285       }
00286       break;
00287       
00288     //====================
00289     // Pulse2
00290     //====================
00291     case 4:
00292       P2Duty = (byData & 0xc0) >> 6;
00293     
00294       if (byData & 0x10)
00295       {
00296         // constant volume
00297         P2Vol = byData & 0xf;
00298       }
00299       else
00300       {
00301         // envelope on
00302         P2Vol = 15;
00303         P2EnvCnt = (byData & 0xf) + 1;
00304       }
00305       break;
00306       
00307     case 5:
00308       P2SwReloadFlag = true;
00309       P2SwLevel = (byData & 7);
00310       break;
00311   
00312     case 6:
00313     case 7:
00314       P2Timer = APU_Reg[7] & 7;
00315       P2Timer = (P2Timer << 8) | APU_Reg[6];
00316       P2WaveLength = (P2Timer+1) * 1000;
00317       P2WavePeriod = P2WaveLength * 8;         
00318       if (wAddr == 7)
00319       {
00320         P2LengthCounter = LengthTable[(byData & 0xf8) >> 3];
00321     
00322         // Reset sequencer
00323         P2Lapse = 0;
00324         P2EnvSeq = P2EnvCnt;
00325         P2Sweep = 0;
00326         P2SwCnt = 0;
00327         P2SwOverflow = false;
00328       }
00329       break;
00330       
00331     //====================
00332     // Triangle
00333     //====================
00334     case 0x8:
00335       LinearCounterReloadFlag = true;
00336       break;
00337       
00338     case 0xA:
00339     case 0xB:
00340       TrTimer = APU_Reg[0xB] & 7;
00341       TrTimer = (TrTimer << 8) | APU_Reg[0xA];
00342       TrWaveLength = TrTimer * 1000;
00343       TrWavePeriod = TrWaveLength * 32;          
00344       
00345       if (wAddr == 0xB)
00346       {
00347         TrLengthCounter = LengthTable[(byData & 0xf8) >> 3];
00348         LinearCounterReloadFlag = true;
00349       }
00350       break;
00351       
00352     //====================
00353     // Noise
00354     //====================
00355     case 0xC:
00356       if (byData & 0x10)
00357       {
00358         // constant volume
00359         NsVol = byData & 0xf;
00360       }
00361       else
00362       {
00363         // envelope on
00364         NsVol = 15;
00365         NsEnvCnt = (byData & 0xf) + 1;
00366       }
00367       break;
00368         
00369     case 0xF:
00370       NsLengthCounter = LengthTable[(byData & 0xf8) >> 3];
00371     
00372       // Reset sequencer
00373       NsEnvSeq = NsEnvCnt;
00374       break;
00375 
00376     case 0xE:
00377       NsFreq = Noise_Freq[byData & 0xf];
00378       break;
00379   
00380     //====================
00381     // DMC(PCM)
00382     //====================
00383     case 0x11:
00384       DmOutLevel = byData & 0x7f;
00385       break;
00386       
00387     //====================
00388     // Control
00389     //====================
00390     case 0x15:
00391       if (byData & 1)
00392       {
00393         P1Enable = true;
00394       }
00395       else
00396       {
00397         P1Enable = false;
00398         P1LengthCounter = 0;
00399       }
00400               
00401       if (byData & 2)
00402       {
00403         P2Enable = true;
00404       }
00405       else
00406       {
00407         P2Enable = false;
00408         P2LengthCounter = 0;
00409       }
00410       
00411       if (byData & 4)
00412       {
00413         TrEnable = true;
00414       }
00415       else
00416       {
00417         TrEnable = false;
00418         TrLengthCounter = 0;
00419       }
00420 
00421       if (byData & 8)
00422       {
00423         NsEnable = true;
00424       }
00425       else
00426       {
00427         NsEnable = false;
00428         NsLengthCounter = 0;
00429       }
00430       break;
00431   } // Switch( wAddr ) 
00432 }
00433 
00434 /*-------------------------------------------------------------------*/
00435 /*  240Hz Clock                                                      */
00436 /*-------------------------------------------------------------------*/
00437 void pNesX_ApuClk_240Hz()
00438 {
00439   //====================
00440   // Pulse1 Envelope
00441   //====================
00442   if (!(APU_Reg[0] & 0x10) && P1EnvSeq > 0)
00443   {
00444     P1EnvSeq--;
00445     if (P1EnvSeq == 0)
00446     {
00447       if (P1Vol > 0)
00448       {
00449         --P1Vol;
00450         P1EnvSeq = P1EnvCnt;
00451       }
00452       else
00453       { 
00454         if (APU_Reg[0] & 0x20)
00455         {
00456           P1Vol = 15;
00457           P1EnvSeq = P1EnvCnt;
00458         }
00459       }
00460     }    
00461   }
00462 
00463   //====================
00464   // Pulse2 Envelope
00465   //====================
00466   if (!(APU_Reg[4] & 0x10) && P2EnvSeq > 0)
00467   {
00468     P2EnvSeq--;
00469     if (P2EnvSeq == 0)
00470     {
00471       if (P2Vol > 0)
00472       {
00473         --P2Vol;
00474         P2EnvSeq = P2EnvCnt;
00475       }
00476       else
00477       { 
00478         if (APU_Reg[4] & 0x20)
00479         {
00480           P2Vol = 15;
00481           P2EnvSeq = P2EnvCnt;
00482         }
00483       }
00484     }    
00485   }
00486     
00487   //====================
00488   // Triangle Linear Counter
00489   //====================
00490   if (LinearCounterReloadFlag)
00491   {
00492     LinearCounter = APU_Reg[0x8] & 0x7f;
00493   }
00494   else if (LinearCounter > 0)
00495   {
00496     LinearCounter--;
00497   }
00498   
00499   if (!(APU_Reg[0x8] & 0x80))
00500   {
00501     LinearCounterReloadFlag = false;
00502   }
00503   
00504   //====================
00505   // Noise Envelope
00506   //====================
00507   if (!(APU_Reg[0xc] & 0x10) && NsEnvSeq > 0)
00508   {
00509     NsEnvSeq--;
00510     if (NsEnvSeq == 0)
00511     {
00512       if (NsVol > 0)
00513       {
00514         --NsVol;
00515         NsEnvSeq = NsEnvCnt;
00516       }
00517       else
00518       { 
00519         if (APU_Reg[0xc] & 0x20)
00520         {
00521           NsVol = 15;
00522           NsEnvSeq = NsEnvCnt;
00523         }
00524       }
00525     }    
00526   }  
00527 }
00528 
00529 /*-------------------------------------------------------------------*/
00530 /*  120Hz Clock                                                      */
00531 /*-------------------------------------------------------------------*/
00532 void pNesX_ApuClk_120Hz()
00533 {
00534   //====================
00535   // Pulse1 Sweep
00536   //====================
00537   if (P1SwReloadFlag)
00538   {
00539     P1Sweep = ((APU_Reg[1] & 0x70) >> 4) + 1;
00540     P1SwCnt = 0;
00541     P1SwReloadFlag = false;    
00542   }
00543   else if (APU_Reg[1] & 0x80 && !P1SwOverflow)
00544   {
00545     P1SwCnt++;
00546     if (P1SwCnt == P1Sweep)
00547     {
00548       P1SwCnt = 0;
00549       
00550       int sweep = P1Timer >> P1SwLevel;
00551       int value = P1Timer;
00552       if (APU_Reg[1] & 8)
00553       {
00554         value = value - sweep - 1;
00555         if (value < 8)
00556         {
00557           P1SwOverflow = true;
00558           return;
00559         }
00560       }
00561       else
00562       {
00563         value = value + sweep;
00564         if (value > 0x7ff)
00565         {
00566           P1SwOverflow = true;
00567           return;
00568         }
00569       } 
00570       P1Timer = value;
00571       
00572       APU_Reg[3] &= 7;
00573       APU_Reg[3] |= value >> 8;
00574       APU_Reg[2] = value & 0xff;
00575        
00576       P1WaveLength = (value + 1) * 1000;
00577       P1WavePeriod = P1WaveLength * 8;         
00578     }
00579   }
00580 
00581   //====================
00582   // Pulse1 Sweep
00583   //====================  
00584   if (P2SwReloadFlag)
00585   {
00586     P2Sweep = ((APU_Reg[5] & 0x70) >> 4) + 1;
00587     P2SwCnt = 0;
00588     P2SwReloadFlag = false;    
00589   }
00590   else if (APU_Reg[5] & 0x80 && !P2SwOverflow)
00591   {
00592     P2SwCnt++;
00593     if (P2SwCnt == P2Sweep)
00594     {
00595       P2SwCnt = 0;
00596       
00597       int sweep = P2Timer >> P2SwLevel;
00598       int value = P2Timer;
00599       if (APU_Reg[5] & 8)
00600       {
00601         value = value - sweep;
00602         if (value < 8)
00603         {
00604           P2SwOverflow = true;
00605           return;
00606         }
00607       }
00608       else
00609       {
00610         value = value + sweep;
00611         if (value > 0x7ff)
00612         {
00613           P2SwOverflow = true;
00614           return;
00615         }
00616       } 
00617       
00618       P2Timer = value;
00619       
00620       APU_Reg[7] &= 7;
00621       APU_Reg[7] |= value >> 8;
00622       APU_Reg[6] = value & 0xff;
00623        
00624       P2WaveLength = (value + 1) * 1000;
00625       P2WavePeriod = P2WaveLength * 8;         
00626       
00627     }
00628   }
00629 }
00630 
00631 /*-------------------------------------------------------------------*/
00632 /*  60Hz Clock                                                       */
00633 /*-------------------------------------------------------------------*/
00634 void pNesX_ApuClk_60Hz()
00635 {
00636   // Pulse1
00637   if (!(APU_Reg[0] & 0x20) && P1LengthCounter > 0)
00638   {
00639     P1LengthCounter--;
00640   }
00641 
00642   // Pulse2
00643   if (!(APU_Reg[4] & 0x20) && P2LengthCounter > 0)
00644   {
00645     P2LengthCounter--;
00646   }
00647 
00648   // Triangle
00649   if (!(APU_Reg[0x8] & 0x80) && TrLengthCounter > 0)
00650   {
00651     TrLengthCounter--;
00652   }
00653   
00654   // Noise
00655   if (!(APU_Reg[0xc] & 0x20) && NsLengthCounter > 0)
00656   {
00657     NsLengthCounter--;
00658   }
00659   
00660 }
00661 
00662 /*-------------------------------------------------------------------*/
00663 /*  Status                                                           */
00664 /*-------------------------------------------------------------------*/
00665 BYTE ApuRead( WORD wAddr )
00666 {
00667   if (wAddr == 0x15)
00668   {
00669     return (P1LengthCounter > 0) | (P2LengthCounter > 0) << 1 | (TrLengthCounter > 0) << 2 | (NsLengthCounter > 0) << 3;
00670   }
00671   
00672   return APU_Reg[ wAddr ];  
00673 }
00674