Apu Emulation

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
User avatar
Anes
Posts: 605
Joined: Tue Dec 21, 2004 8:35 pm
Location: Mendoza, Argentina

Apu Emulation

Post by Anes » Sun Jan 23, 2005 7:50 pm

well, after al the advices and help from you guys i made a simple thing to hear sounds. I think its a good starting point.
i check when the channel timer register is written, and take the 11 bit wavelenght and convert it to herz with the following formula:
hz = 1789772.5 / DIVIDER / WaveLenght

Where DIVIDER is 16 for square and 32 for Triangle (i guess), then thanks to Charles Petzold "FillBuffer" function expample form his "Programming Windows" Book, i convert "hz" to PCM format.

The result? sound.. but very poor. I know disch told me about a way of taking all the outputs to sample correctly: as APU does.

But i didnt understand you well disch, if you could explain it to me very "step-by-step" i will preciate it.
ANes

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Sun Jan 23, 2005 8:11 pm

Calculating to Hz is a bad idea. Since the APU revolves around cpu cycles... you just have to execute the cycles as they come.

To emulate a Square Wave channel... you'll need vars to track the following (my 'aka' is what I'll be referring to those vars as later):

- The 11-bit written wavelength value (aka nProgTimer)
- A counter to 'count down' that wavelength value (aka nProgCount)
- The current Duty cycle (2,4,8, or 12... representing 12.5%, 25%, 50%, and 75% duty cycles respectively) (aka nDutyCycle)
- A counter to track the steps through those duty cycles (aka nDutyCount)


You'll need to do the following for every 1 CPU cycle (assuming channel is active):

- if nProgCount is zero...
--- reload nProgCount with nProgTimer
--- increase nDutyCount by 1 (wrap at 15 ... 15->0)
- else, nProgCount is not zero...
--- decrease nProgCount by 1


the APU's output for this cycle is found by checking the following:

* if the channel is silenced by the Length Counter, or Sweep Unit --- output is 0
* else if nDutyCount is greater than or equal to nDutyCycle (in the 'negative' portion of the duty cycle) --- output is 0
* else, output is whatever the current Volume level is


now note... if using the simplest, low quality method of downsampling I mentioned earlier... you'll only be taking the output after every ~40.58 CPU cycles

User avatar
Anes
Posts: 605
Joined: Tue Dec 21, 2004 8:35 pm
Location: Mendoza, Argentina

apu

Post by Anes » Sun Jan 23, 2005 10:43 pm

thanks disch, i will try implement that.
ANes

Post Reply