.png)
The game has a title screen, three lives and an ending, and it makes no sound whatsoever. S27 went through the sound chip register by register and nothing since has used it.
That is not an oversight you can fix by dropping S27's example into the game. S27's program makes a note and then waits for it to finish, which is fine when making the note is all the program has to do. A game cannot wait. Twenty milliseconds from now there is a frame to draw whether the explosion has finished or not.
So the question this section answers is not "how do I make a noise" - you know that. It is where a noise goes in a loop that cannot stop.
The shape is one you have used twice already. S24 lit the backdrop on a collision and let a one-byte timer put it out. S29 called that one of the three pieces of glue a game needs. A sound is the same object with a different output:
Nothing waits. The noise lasts as long as the counter says, and the frame it started in was no slower than any other.
Three events want three noises: the shot, a rock coming apart, and a rock reaching the ship. Each is four numbers - what to put in the mixer, which period register to use, what to put in it, and how many frames it lasts:
; Each entry: the mixer byte, which period register, what to put in it,
; and how many frames it lasts.
sounds: DEFB 126, 0, 100, 6 ; 0 shot - tone A, 1250 Hz, an eighth of a second
DEFB 119, 6, 10, 10 ; 1 rock - noise, short and bright
DEFB 119, 6, 25, 25 ; 2 ship - noise, lower, as long as the flash
This is S13's table doing what S13 said tables are for. There is no formula connecting a shot to 1250 Hz; somebody chose it because it sounded right. Adding a fourth sound is four bytes, and tuning the third one is one byte.
The 100 is a period, not a pitch. S27's formula turns it into one: 2,000,000 / (16 x 100) = 1250 Hz. The 200 in the edit at the end of this section is 625 Hz, an octave down, which is a good way to feel what that division is doing.
; --- start sound number A
sfx: ADD A,A
ADD A,A ; four bytes an entry
LD L,A
LD H,0
LD DE,sounds
ADD HL,DE
LD A,7 ; the mixer, with the keyboard's bits kept
OUT (2),A
LD A,(HL)
OUT (3),A
INC HL
LD A,(HL) ; which period register
OUT (2),A
INC HL
LD A,(HL) ; and the period
OUT (3),A
INC HL
LD A,8 ; channel A, full volume
OUT (2),A
LD A,15
OUT (3),A
LD A,(HL) ; how long it lasts
LD (soundT),A
RET
Two ADD A,A because the entries are four bytes each - S14's doubling, done
twice. Then the same walk along HL you have written a dozen times.
; --- every frame: count the sound down, and stop it when it runs out
sound: LD A,(soundT)
OR A
RET Z
DEC A
LD (soundT),A
RET NZ
; --- volume off, mixer back to silence
silence:
LD A,8
OUT (2),A
LD A,0
OUT (3),A
LD A,7
OUT (2),A
LD A,127
OUT (3),A
RET
sound goes into the playing state's list of things that happen every frame,
after flash. Most frames it does nothing but read a zero and return, which
is exactly the cost you want a silent frame to have.
Note that silence is two writes, not one. Volume 0 is genuinely silent -
not quiet, silent - and turning the mixer bits back off as well means the next
sound starts from a known state rather than from whatever the last one left.
silence gets its own label because the title and game over screens call it
directly: a game that dies mid-explosion should not take the explosion with
it to the menu.
There is one trap here and it is worth meeting deliberately, because when it bites the symptom looks nothing like a sound problem.
Register 7 is not only the mixer. Its top two bits say which way the chip's two IO ports face, and the keyboard matrix from S25 depends on them: the game sets that register to 127 at start-up to make port A an output and port B an input, and reads the matrix through registers 14 and 15 ever afterwards.
So every mixer value in the table has to keep those two bits as they were.
127 is 01111111; 126 is 01111110, tone A on; 119 is 01110111, noise A
on. Both leave bit 6 set and bit 7 clear.
Write a mixer byte that forgets them - 00000110, say, which looks like a
perfectly reasonable "tone A and noise A on" - and the sound comes out exactly
right while the keyboard stops answering. Hold a direction and the ship
does not move.
The nasty part is how long it lasts. That wrong value is only in the register
until the sound ends and silence puts 127 back, so the controls do not die -
they go deaf for the six frames of a shot and then recover, every time you
fire. What reaches the player is not a broken game but a sluggish one, and
nothing on screen connects it to the sound chip. Leave the same byte in
permanently and the controls stop dead, which is at least easier to find.
All three sit where the event already happens, and none of them is more than two lines.
The shot, in fire, before anything else - a bullet that cannot be fired
should not make a noise:
fire: LD A,(bullA)
OR A
RET NZ
LD A,0 ; the shot
CALL sfx
The rock coming apart, in hits, beside the score it already awards:
CALL score1 ; ...score...
LD A,1 ; ...a noise...
CALL sfx
CALL spawn ; ...next rock
And the ship being hit, beside the flash it already starts:
LD A,25 ; the rock reached the ship: half a second of red
LD (flashT),A
LD A,2 ; and a longer, lower noise
CALL sfx
CALL spawn
That last pair is worth looking at twice. The flash lasts 25 frames and so does the sound, because they are the same event told twice - once to the eyes and once to the ears. They are two separate timers counting the same number, which is a small redundancy you could remove and probably should not: the day you want the sound to outlast the flash, you change one number.
Everything above goes into S31's program. Five small additions:
sounds, sfx, sound and silence as printed above, plus
soundT: DEFB 0 with the other variables.CALL sound joins the playing state's every-frame list, after flash.CALL sfx at the three events.CALL silence on the way into the title and game over states.What you should see
Nothing new. That is the point - the screen is exactly as S31 left it.
What you should hear: a short high blip every time you fire, a brighter crack when a rock comes apart, and a longer, lower one when a rock reaches the ship, arriving at the same moment the screen goes red. The title screen and the game over screen are silent.
And the game runs at the same speed it did before. If it does not, something is waiting that should be counting.
CALL sound from the playing state's list. Fire once. What stops?
What does that tell you about how much of this section is the starting and
how much is the stopping?32.1 - A sound for starting. Play something when a game begins. The
trouble is that newgame is called from the title state, and the sound would
be cut off the moment the playing state runs silence... except that it is
not. Work out why, then decide whether you want it to be.
32.2 - Falling pitch. Make the ship-hit sound slide downwards over its 25
frames instead of sitting on one note. sound already runs every frame and
already knows how much time is left.
32.3 - Two things at once. The chip has three channels and this section
uses one. Give the shot its own channel so that firing while a rock explodes
does not cut the explosion short. What has to change in sfx, and what has
to change in sound?
32.4 - A quieter game. Volume 15 for everything is a blunt instrument. Add a volume column to the table and pick a level per sound. Which of the three wants to be loudest, and is that the one you expected?
32.5 - Silence on purpose. Add a key that mutes the game. It should stop sound that is already playing, and it should not cost a frame.
| Symptom | Cause |
|---|---|
| The sound is right and the controls have stopped working | A mixer byte that did not preserve register 7's top two bits. The keyboard matrix reads through the same register. |
| The controls feel sluggish, and only while the game is making a noise | The same fault, but the wrong value is being put back by silence a few frames later. Input is lost for exactly as long as each sound lasts. |
| One shot and then a note that never stops | Nothing is counting it down. sound has to run every frame, not only when something is sounding. |
| The note is wrong and the assembler warned about a value | A period over 255 will not fit the table's byte. Tone period is 12 bits across two registers; this table only fills the low one. |
| The explosion is silent but the shot works | The mixer enables tone and noise with different bits - bit 0 and bit 3. Enabling the wrong one leaves the generator running with nothing routed to the output. |
| A sound carries on into the title screen | The states that do not run sound have to call silence themselves. |
| The game slows down when it makes a noise | Something is waiting for the sound rather than counting frames while it plays. |
S33. Where You Go From Here. The game has a beginning, an end and a voice, which means the course has run out of things it set out to teach. What is left is what you can now do with it, what this course deliberately did not cover, and where the ground is still unbroken.