aboutsummaryrefslogtreecommitdiff
path: root/sound/beep.s
diff options
context:
space:
mode:
Diffstat (limited to 'sound/beep.s')
-rw-r--r--sound/beep.s84
1 files changed, 62 insertions, 22 deletions
diff --git a/sound/beep.s b/sound/beep.s
index cae6ec6..f9ab7a8 100644
--- a/sound/beep.s
+++ b/sound/beep.s
@@ -101,36 +101,70 @@
lda #$0F
sta APU::m_status
- ;; This was the configuration for the APU. Now let's produce sound on the
- ;; Square 1 channel (registers: $4000-$4003).
-
- ;; Let's configure the Square 1 channel before producing any sound.
+ ;; This was the configuration for the APU. After that, all enabled channels
+ ;; can produce output and it's going to be combined using a non-linear
+ ;; mixing scheme (https://www.nesdev.org/wiki/APU_Mixer). Long story short:
+ ;; all channels work independently and they are going to be properly
+ ;; combined in the end, so a programmer can focus on each channel
+ ;; separately.
+
+ ;; This example uses the Square 1 channel to produce sound. Let's see how it
+ ;; can be configured, and 'sound/select.s' will deal with the other
+ ;; channels.
+ ;;
+ ;; The $4000 register configures how the square 1 channel will produce its
+ ;; output via the envelope. The **envelope** is a hardware feature that
+ ;; automatically controls the volume of a sound over time. The APU has two
+ ;; modes of operation: constant volume or decay envelope mode. This is
+ ;; regulated via bit 4 of the $4000 register: 1 for constant volume; 0 for
+ ;; decay envelope mode. With this in mind, the low nibble of this register
+ ;; works like this:
;;
- ;; The low nibble controls the volume: 0 for silent, 1 very low, F
- ;; maximum. Just to be annoying we will be setting the volume at a maximum
- ;; level.
+ ;; - Bit 4 = 1: the low nibble contains the **volume** of the sound, which
+ ;; will be frozen in time (i.e. constant). Set the volume to
+ ;; 0 for a silent channel; 1 for very low volume, F for
+ ;; maximum volume.
+ ;; - Bit 4 = 0: the low nibble contains the **period** of decay of the
+ ;; volume. That is, it makes the volume to decay every V
+ ;; frames, where V is the value on the low nibble. This is in
+ ;; the end possible because of the internal frame counter
+ ;; from the APU. In this context, the volume will always
+ ;; start at F, and it will decay until reaching zero at the
+ ;; given period. With all of that in mind, then, note that
+ ;; higher number means decaying the volume slower.
;;
- ;; Then we have two bits which seem quite odd at first. Bit 4 sets/unsets
- ;; whether the volume is to be kept constant. If unset, then an internal
- ;; counter will tune it down when running out. Similarly, bit 5 sets/unsets
- ;; whether the length counter is to be accounted or not. See this counter
- ;; down below. All in all, here we make the sound constant, so the beep
- ;; never stops until we mute it (which we don't in this example).
+ ;; Another important bit is 5, which is the "looping" bit. That is, whether
+ ;; the configured envelope should be repeated after a shot has been done. If
+ ;; this bit is set to 1, then this envelope will be repeated over and
+ ;; over. If, otherwise, the bit is set to 0, then it's a one-shot note. For
+ ;; how long this one-shot should happen? This is regulated in $4003 as you
+ ;; will see below.
;;
- ;; Finally we have the "duty" bits, which has four possibilities
- ;; available. These regulate the tone for the note, and it's basically the
- ;; percentage of time the square wave is in the "up" position.
+ ;; With this in mind note that in this example we set these two bits to
+ ;; 1. Hence, it's an continuous beep, and the low nibble contains the volume
+ ;; that will be constant over time. The value for the volume is F, hence
+ ;; maximum volume.
+ ;;
+ ;; Finally we have the two most significant bits, which configure the "duty"
+ ;; cycle. That is, on a square wave, what percentage of time should the wave
+ ;; spend in the "up" position. We have two bits, so we can select four
+ ;; different configurations of the wave's shape (see:
+ ;; https://www.nesdev.org/wiki/APU_Pulse). Here we pick "10", which
+ ;; corresponds to the 50% duty cycle, meaning that we want a square wave
+ ;; that spends the same amount of time up and down.
lda #%10111111
sta APU::m_square_1_envelope
;; The $4001 address contains the sweep register, which is a way that the
;; APU has in order to produce different pitch effects, or workaround known
- ;; issues on some tones. I'm not touching it here other than resetting to 0.
+ ;; issues on some tones. Configuring the sweep register so we tend towards
+ ;; lower frequencies (longer periods) can also be a way of muting the
+ ;; channel. I'm not touching it here other than resetting to 0.
lda #0
sta APU::m_square_1_sweep
;; The note to play is 11 bits long. This means that we need two bytes for
- ;; it, which for square 1 are $4002 and $4003. $4002 is the least
+ ;; it, which for square 1 are $4002 and $4003. $4002 contains the least
;; significant bits, and the three least significant bits from $4003 are the
;; most significant bits from this 11-bit note definition. How to know which
;; note corresponds to what value is easy via:
@@ -139,10 +173,16 @@
;;
;; The 5 other bits from $4003 correspond to the length counter. That is,
;; you can regulate for how long this note has to be reproduced, and the APU
- ;; will (magically) track things for you. This is not available on this
- ;; configuration because we disabled the option when we configured
- ;; 'APU::m_square_1_envelope' (see above). Hence, we will set these bits to
- ;; 0.
+ ;; will (magically) track things for you. Funnily enough, this "counter" is
+ ;; not really a counter, but an index to a table with the actual values. See
+ ;; the table here: https://www.nesdev.org/wiki/APU_Length_Counter. This is
+ ;; not available on this configuration because we disabled the option when
+ ;; we configured 'APU::m_square_1_envelope' (see above). Hence, we will set
+ ;; these bits to 0.
+ ;;
+ ;; NOTE: one gotcha from writing into $4003 is that it implicitely resets
+ ;; the envelope. Hence, if you write to this every frame for whatever
+ ;; reason, then you will never notice the fading.
lda #$C9
sta APU::m_square_1_low
lda #$00