![]() |
Sounds: the tunes and the effects |
A 48K Spectrum has one bit of sound hardware, bit 4 of port $FE, and Nightshade makes every sound by setting and clearing it with the processor counting in between, the border kept black. While a sound plays nothing else happens: the game stands still for a tune, and every beep lengthens the turn it is in. There are two players. The tunes go through PLAY_NOTE, a note at a time, from a table of notes; everything else is waves from CLICK, which turns the speaker on for B turns of a 13 T-state loop and off for as long, most of them through BEEP ($C46A, the end of ARRIVE_SOUND), which repeats it C times. Almost all of it is code Pentagram has too, a few bytes apart -- Nightshade came first, so Pentagram's unused sound routines are Nightshade's, left behind -- and the tune player and its notes are Knight Lore's and Alien 8's as well (the table at the foot of the page). The pause, which in Pentagram and Alien 8 beeps, is silent here: a pause in the running game -- SPACE held into the end of a turn for 0.3 s, let go 0.5 s, held 0.3 s and let go -- the game waiting in PAUSE all the while, made no edge at all.
Every recording here is the game's own code running in SkoolKit's simulator, each change of the speaker bit logged to the T-state and rendered at 44100 Hz; nothing is synthesised. What is heard in play was recorded from the game playing, a turn at a time, with whatever else sounded in those turns; to keep each recording to its subject the monsters were cleared away and the villains' drawn flags cleared at the start of every turn, except where they are the subject. Each recording is checked wave by wave against a model of the turn worked out from the code -- which records' routines sound, and at what half-wave count, given the state the turn began in -- and every off half-wave against the instruction timings of the loop that made it. The simulator has no memory contention, so all of this runs a touch faster, and a touch higher, than on a real Spectrum. A quiet turn (the knight standing, nothing else about) lasts 223,383 T-states, 64 ms; the calls that stand for a sound made over several turns are spaced by that.
A tune is a string of note bytes ended by $FF: the low six bits index NOTES, five octaves of semitones from G#1 to G6 with 0 a rest (no tune has one), and the top two bits are the length less one, one to four units of about 0.155 s. PLAY_TUNE plays a tune through; PLAY_TUNE_ONCE plays the menu's once, stopping at a key. Six tunes, none of them another Ultimate game's (compared at this build with Pentagram, Alien 8, Knight Lore).
TUNE_CONTROL_CHOSEN, three notes played through by PLAY_TUNE from the menu (MENU) whenever a pass round its loop leaves CONTROL changed -- a key 1 to 4 choosing a control method, or 5 turning directional control on or off. Alien 8's menu beeps there instead.
Recorded: PLAY_TUNE called with DE = $C7F4.
Checked: 3 notes, 3 units, no rests, from row 42 (C#5, 553 Hz) to row 49 (G#5, 824 Hz). The table's wave counts make 322 waves, so 643 edges (the first OUT turns off a speaker already off); the recording has 643 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
475 ms, 643 edges, 553 to 824 Hz (C#5 to G#5).
TUNE_GAME_START, played through by NEW_GAME once 0 has been pressed on the menu, before the play screen is drawn: the game stands still while it plays.
Recorded: PLAY_TUNE called with DE = $C7F8.
Checked: 10 notes, 10 units, no rests, from row 37 (G#4, 415 Hz) to row 49 (G#5, 824 Hz). The table's wave counts make 885 waves, so 1,769 edges (the first OUT turns off a speaker already off); the recording has 1,769 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
1.58 s, 1,769 edges, 415 to 824 Hz (G#4 to G#5).
TUNE_NEW_LIFE, five falling notes, played by NEW_LIFE when a life has ended and there is another to come -- not for the first life of a game, and not when none are left (below, a life lost, where it is heard in its place).
Recorded: PLAY_TUNE called with DE = $C839.
Checked: 5 notes, 6 units, no rests, from row 33 (E4, 331 Hz) to row 37 (G#4, 415 Hz). The table's wave counts make 344 waves, so 687 edges (the first OUT turns off a speaker already off); the recording has 687 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
942 ms, 687 edges, 331 to 415 Hz (E4 to G#4).
TUNE_GAME_OVER, played by GAME_OVER once the percentage and the score are on the screen -- after the last life, and after the last villain too: the ending comes after it (below, the ending).
Recorded: PLAY_TUNE called with DE = $C803.
Checked: 22 notes, 22 units, no rests, from row 25 (G#3, 209 Hz) to row 45 (E5, 656 Hz). The table's wave counts make 1,188 waves, so 2,375 edges (the first OUT turns off a speaker already off); the recording has 2,375 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
3.46 s, 2,375 edges, 209 to 656 Hz (G#3 to E5).
TUNE_ENDING, played by ENDING_PIT_FRONT when the last of the four villains has sunk into the pit; then the menu.
Recorded: PLAY_TUNE called with DE = $C81A.
Checked: 30 notes, 31 units, no rests, from row 30 (C#4, 279 Hz) to row 45 (E5, 656 Hz). The table's wave counts make 2,314 waves, so 4,627 edges (the first OUT turns off a speaker already off); the recording has 4,627 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
4.88 s, 4,627 edges, 279 to 656 Hz (C#4 to E5).
The notes the tunes use, worked out from NOTES and PLAY_NOTE's timing: B and C time each half-wave, and a unit is that many waves. The table has 61 rows; these are the ones any of the six tunes plays.
| Row | Note | B | C | Waves a unit | Pitch | A unit | Played in |
|---|---|---|---|---|---|---|---|
| 18 | C#3 | 243 | 4 | 22 | 132.3 Hz | 0.166 s | The menu |
| 21 | E3 | 35 | 4 | 26 | 166.2 Hz | 0.156 s | The menu |
| 23 | F#3 | 203 | 3 | 29 | 186.7 Hz | 0.155 s | The menu |
| 24 | G3 | 163 | 3 | 31 | 197.7 Hz | 0.157 s | The menu |
| 25 | G#3 | 125 | 3 | 33 | 209.3 Hz | 0.158 s | The menu, Game over |
| 26 | A3 | 89 | 3 | 35 | 221.8 Hz | 0.158 s | The menu, Game over |
| 29 | C4 | 250 | 2 | 41 | 263.3 Hz | 0.156 s | The menu, Game over |
| 30 | C#4 | 221 | 2 | 44 | 279.2 Hz | 0.158 s | The menu, Game over, The end of the ending |
| 32 | D#4 | 169 | 2 | 49 | 312.9 Hz | 0.157 s | The menu, Game over, The end of the ending |
| 33 | E4 | 145 | 2 | 52 | 331.4 Hz | 0.157 s | The menu, A new life, Game over, The end of the ending |
| 34 | F4 | 123 | 2 | 55 | 350.4 Hz | 0.157 s | A new life |
| 35 | F#4 | 102 | 2 | 58 | 370.6 Hz | 0.156 s | The menu, A new life, The end of the ending |
| 36 | G4 | 81 | 2 | 62 | 393.4 Hz | 0.158 s | A new life, The end of the ending |
| 37 | G#4 | 63 | 2 | 65 | 415.2 Hz | 0.157 s | The menu, A game starting, A new life, Game over, The end of the ending |
| 38 | A4 | 45 | 2 | 69 | 439.6 Hz | 0.157 s | The menu, The end of the ending |
| 39 | A#4 | 28 | 2 | 73 | 465.5 Hz | 0.157 s | A game starting |
| 40 | B4 | 12 | 2 | 78 | 492.7 Hz | 0.158 s | The menu, The end of the ending |
| 41 | C5 | 253 | 1 | 82 | 523.1 Hz | 0.157 s | The menu, A game starting |
| 42 | C#5 | 239 | 1 | 87 | 553.2 Hz | 0.157 s | The menu, A control method chosen, A game starting, Game over, The end of the ending |
| 44 | D#5 | 213 | 1 | 98 | 619.4 Hz | 0.158 s | The menu, The end of the ending |
| 45 | E5 | 201 | 1 | 104 | 655.6 Hz | 0.159 s | The menu, A control method chosen, A game starting, Game over, The end of the ending |
| 47 | F#5 | 179 | 1 | 117 | 734.2 Hz | 0.159 s | The menu |
| 49 | G#5 | 159 | 1 | 131 | 824.1 Hz | 0.159 s | The menu, A control method chosen, A game starting |
Four short effects are played a note a turn: whatever starts one sets EFFECT_TIME to its length and EFFECT to which, and at the end of every turn EFFECT_NOTE plays twelve waves (ARRIVE_SOUND's BEEP) at the note EFFECT_TIME points to in EFFECT_TABLE, then counts it down. The notes run backwards, from the byte the count points to down to the one after the effect's address, so the byte at each address is never played by its own effect -- it is the previous effect's first note. A new effect simply replaces one under way. The table's notes are Pentagram's too, twenty bytes the same, but Pentagram only ever starts the first two.
| Effect | Its address | Notes | Half-wave counts, as played | Pitches (Hz) | Started by |
|---|---|---|---|---|---|
| 0 | $C3E0 | 4 | 80, 112, 64, 112 | 1619, 1169, 2005, 1169 | VISIT_CELL: a new cell with a building |
| 1 | $C3E4 | 5 | 64, 64, 80, 96, 112 | 2005, 2005, 1619, 1358, 1169 | SPEED_BONUS: the speed bonus |
| 2 | $C3E9 | 7 | 48, 128, 128, 128, 112, 96, 80 | 2632, 1026, 1026, 1026, 1169, 1358, 1619 | HITS_BONUS: the cure |
| 3 | $C3F0 | 4 | 14, 96, 48, 96 | 7848, 1358, 2632, 1358 | OBJECT_LYING: an object taken up |
VISIT_CELL, as the knight's walk takes him into a cell he has not been in before that has something built on it, sets EFFECT_TIME to four notes of effect 0; EFFECT_NOTE plays one a turn at the end of the turn, twelve waves each, from the last of the effect's bytes back. Open ground (type 0) is marked visited without a sound, and a cell is only new once a game: VISITED is what the percentage at the end counts. His footsteps are heard under it.
Recorded: the game running: a life started in open cell (3, 1) by the game's own restart, the first open cell with a building next along +U, and walk held along +U from its middle until he crossed into (4, 1); recorded from the first step to two turns after the last note.
Checked: 68 waves from CLICK (the code implies 68); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.86 s, 136 edges, 23 turns, 1169 to 2005 Hz.
SPEED_BONUS, as the knight touches the bonus of graphic 2: 255 turns of a top speed of 18 and sound effect 1, five notes, one a turn (EFFECT_NOTE). The bonus becomes a puff (graphic 12, VANISHING), whose four frames end with its crackle (PUFF_SOUND) if it was on the screen.
Recorded: the game running, the knight standing in the cell a new game gave him: a bonus of graphic 2 laid where he stands at the start of a turn (build_nightshade's _beside_knight, as its sessions lay them); nine turns from that one.
Checked: 76 waves from CLICK (the code implies 76); every on half-wave was 13B + 18 T-states for the B the code gives it; the puff's pitches were sixteen bytes in a row of the ROM's first 8K, less their top bits, as PUFF_SOUND reads them; every off half-wave inside a sound was 13B plus 64 (BEEP's), 133 (PUFF_SOUND's), the loops' own timing.
634 ms, 152 edges, 9 turns, 1087 to 15284 Hz.
HITS_BONUS, as he touches the bonus of graphic 3 (a flask): all three hits of the life back, and sound effect 2, seven notes, one a turn; then the bonus's crackle, as for the other.
Recorded: the game running, the knight standing in the cell a new game gave him: a bonus of graphic 3 laid where he stands at the start of a turn (build_nightshade's _beside_knight, as its sessions lay them), his hits first set to one; nine turns from that one.
Checked: 100 waves from CLICK (the code implies 100); every on half-wave was 13B + 18 T-states for the B the code gives it; the puff's pitches were sixteen bytes in a row of the ROM's first 8K, less their top bits, as PUFF_SOUND reads them; every off half-wave inside a sound was 13B plus 64 (BEEP's), 133 (PUFF_SOUND's), the loops' own timing.
660 ms, 200 edges, 9 turns, 1021 to 17241 Hz.
OBJECT_LYING, as the knight touches one of the four objects lying in the town: sound effect 3, four notes, and the object taken up (PICK_UP_THING). Effect 3's address is the table's last and its notes run past the table's end: the first one played, the fourth byte after the address, is the first byte of FIRE_SOUND's code, $0E (LD C,$20), a half-wave count of 14 where the rest of the table's are 48 to 128 -- a squeak three times the pitch of anything else in the table, before three ordinary notes. Pentagram has the same table and the same squeak, but never starts effect 3 (Pentagram's sounds).
Recorded: the game running, the knight standing where a new game put him: the object of graphic 7 (the first object record's) laid where he stands at the start of a turn, as the build's sessions lay it; six turns from that one.
Checked: 48 waves from CLICK (the code implies 48); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
409 ms, 96 edges, 6 turns, 1358 to 7848 Hz.
FOOTSTEP, called by WALK_ON for every step of the walk: FOOTSTEPS counts them, and at a walking speed (under 12) every fourth step plays four waves, at a half-wave count of 64 and 96 by turns (bit 2 of the count): a tick and a tock, a step a turn, so one every four turns. From standing his speed goes 4, 6, 8 over three turns and stays at 8. Pentagram's footstep, with four waves where Pentagram's plays two.
Recorded: the game running: a life started in open cell (1, 2), with open ground for two cells along +U; walk held for 24 turns, and two turns after.
Checked: 24 waves from CLICK (the code implies 24); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.60 s, 48 edges, 26 turns, 1358 to 2005 Hz.
FOOTSTEP at a speed of 12 or more, which only the speed bonus gives (SPEED_BONUS, a top speed of 18 for 255 turns): a footstep every second step, pitched by bit 1 of the count, so the ticks come twice as often and he covers ground faster between them. Pentagram's footstep has no faster half.
Recorded: the game running in open cell (1, 2): the speed bonus laid where he stood and taken, nine turns for its sound and its puff to finish, then walk held for 24 turns, and two after.
Checked: 48 waves from CLICK (the code implies 48); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.71 s, 96 edges, 26 turns, 1358 to 2005 Hz.
UPDATE_TOP, the knight's top, when the legs' move met a wall this turn: his arms thrown out (graphic 22 or 30) and BUMP_SOUND, sixteen single waves at a half-wave count of (C XOR $A5) + C for C = 16 down to 1, so the pitch jumps about -- Pentagram's jump sound, with a test in front that keeps it quiet for anything not on the screen. Once his arms are out it does not sound again while he walks on into the wall; his feet go on ticking. The monsters of graphics 112-127 make the same sound at a wall (MONSTER112_UPDATE).
Recorded: the game running from where a new game put him: walk held along +U until he met a wall, four turns on into it, and two after; the last dozen turns kept.
Checked: 24 waves from CLICK (the code implies 24); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), 83 (BUMP_SOUND's), the loops' own timing.
840 ms, 48 edges, 12 turns, 670 to 2005 Hz.
FIRE_SOUND, from KNIGHT_THROWS as an antibody (or an object) leaves his hand: 32 single waves, each half-wave count the step count less the one before. The count it starts from is the B KNIGHT_THROWS set to redraw the emptied place on the panel, 11 less the place's index (the first place, at the bottom, is 0), which the drawing leaves in B. From the second place (B = 10) the counts go 22, 9, 21, 8, 20, 7 ... 8, 251, 7, 250: two sequences interleaved, each a step shorter every other wave, so two rising notes at once, the second running down through 1 to 0 -- 256, the lowest count there is -- and on from 255, so that half-way through it drops to the bottom and climbs again. Each place throws with a slightly different sound. Each antibody flies on until it meets a wall, becomes a puff (VANISHING), and as the puff ends crackles (PUFF_SOUND): sixteen single waves at pitches read from the ROM, from an address made of the random number, so no two puffs sound alike. Pentagram's bolt and its puff, sixteen waves long here where Pentagram's is four.
Recorded: the game running where a new game put him, facing the wall along +U: antibodies (things 5 and 6) put in the first two carried places, fire (Q) pressed for a turn, three turns off, pressed for another -- the second place throws first, then the first -- and on until both puffs had gone.
Checked: 96 waves from CLICK (the code implies 96); every on half-wave was 13B + 18 T-states for the B the code gives it; the 2 puffs' pitches were sixteen bytes in a row of the ROM's first 8K, less their top bits, as PUFF_SOUND reads them; every off half-wave inside a sound was 13B plus 76 (FIRE_SOUND's), 133 (PUFF_SOUND's), the loops' own timing.
994 ms, 192 edges, 14 turns, 514 to 29167 Hz.
His end, as KNIGHT_KILLED ($CEC4, in WANDERING_MONSTER) makes it: both his records become puffs (graphic 12, VANISHING), and after their four frames each crackles in turn (PUFF_SOUND, sixteen waves from the ROM, legs then top). With a life to come, NEW_LIFE plays the new-life tune and puts him back in the cell he died in (KNIGHT_KILLED copies it into the start records); then for seventeen turns, while ARRIVING climbs from 40 by two a turn, ARRIVE_SOUND plays eight waves a turn at its complement -- 213 down to 181 -- a note that rises as he appears. The first life of a game appears the same way, without the tune.
Recorded: the game running where a new game put him: his end begun at the start of a turn the way KNIGHT_KILLED ends a life (build_nightshade's _kill: both records to the puff graphic), five lives; recorded to two turns after he stood there again.
Checked: 167 waves from CLICK (the code implies 167); every on half-wave was 13B + 18 T-states for the B the code gives it (the first wave after a tune left out: the tune leaves the speaker on, so that wave's rising edge is no edge); the 2 puffs' pitches were sixteen bytes in a row of the ROM's first 8K, less their top bits, as PUFF_SOUND reads them; every off half-wave inside a sound was 13B plus 64 (BEEP's), 133 (PUFF_SOUND's), the loops' own timing; and the tune at $C839: the table's wave counts make 344 waves, so 687 edges (the first OUT turns off a speaker already off); the recording has 687 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply.
2.64 s, 1,022 edges, 24 turns, 623 to 17241 Hz.
APPEARING_UPDATE: a new monster spends four turns appearing, graphics 128 to 131, and on each, if it was on the screen the turn before, APPEAR_SOUND plays six waves at the complement of its graphic turned right three bits, the top three kept: 224, 192, 160 and 128 -- four notes rising. Then it is a monster of the kind of the villain nearest it. SPAWN_MONSTER starts one every fourth turn in the knight's cell or one next to it, so in play this is heard whenever one appears in sight. The same bytes are Pentagram's, left over as data there (Pentagram's sounds).
Recorded: the game running where a new game put him: at the point in a turn where SPAWN_MONSTER starts a monster, one started as it does -- its record copied from the template it copies, put in the knight's cell -- 48 units from him along U; recorded until it had become a monster.
Checked: 24 waves from CLICK (the code implies 24); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
366 ms, 48 edges, 5 turns, 593 to 1026 Hz.
Every 256th turn SPAWN_CREATURE puts the creature (graphics 136-139) at a random place in the knight's cell, and it makes for him, two units a turn each way (CREATURE_UPDATE). Every turn it was on the screen the turn before, BLIP_BY_TURN blips: twelve waves at one of the sixteen pitches of BLIP_PITCHES, picked by the turn counter's low four bits -- counts of 32, 32, 48, 64, 80 over and over, a figure falling in pitch (sixteen is not a multiple of five, so where the counter comes round the top note is held three turns). When it touches him it bursts and takes one of his hits (WANDERING_MONSTER), and its puff crackles as it ends (PUFF_SOUND). Pentagram has the routine and all five tables, the same bytes, and never reaches them (Pentagram's sounds).
Recorded: the game running, the knight standing where a new game put him: the turn counter set to 255 at the start of a turn, so that the turn's count made it the 256th and SPAWN_CREATURE did its work; recorded until the creature had burst and its puff had gone. His hits went from 3 to 2.
Checked: 196 waves from CLICK (the code implies 196); every on half-wave was 13B + 18 T-states for the B the code gives it; the puff's pitches were sixteen bytes in a row of the ROM's first 8K, less their top bits, as PUFF_SOUND reads them; every off half-wave inside a sound was 13B plus 64 (BEEP's), 133 (PUFF_SOUND's), the loops' own timing.
1.46 s, 392 edges, 20 turns, 1029 to 19774 Hz.
A villain on the screen hums: every turn it was drawn the turn before, VILLAIN_WANDER enters the creature's blip routine at BLIP_FROM_TABLE ($C335) for twelve waves at a pitch picked by the turn counter's low four bits. It was meant, by every sign, to give each villain its own sixteen pitches (an inference, but a safe one): BLIP_PITCHES holds five tables of sixteen, the creature's and then four more, VILLAIN_PITCHES ($C35D), and VILLAIN_WANDER loads that address into HL and an offset by the villain's kind into BC before the call. Both halves are wrong. BLIP_FROM_TABLE builds its address as the turn's four bits in HL plus BC -- it overwrites HL, so the table's address is lost and BC alone is the base; and the offset, the graphic turned left two bits and ANDed with $F0, keeps the graphic's bit 5, which every villain (96-111) has set, in bit 7: it comes to $80, $90, $A0 or $B0, not 0, 16, 32 or 48 (AND $30 would have been right). So each villain hums sixteen bytes of the ROM, from $0080 to $00BF, and the four tables are never read. That part of the ROM is the end of a routine and, from $0095, the table of BASIC keywords, so three of the four villains hum mostly the spelling of keywords. (The mistakes read from the code; the pitches heard in the runs below, whose every wave matched the ROM's bytes.)
| Villain graphics | BC as VILLAIN_WANDER makes it | What it hums (ROM) | Its own table, never read |
|---|---|---|---|
| 96-99 | $80 | $0080-$008F: 254 13 200 254 16 216 254 24 63 216 35 254 22 56 1 35 (ROM code) | $C35D: 32 32 48 48 64 64 80 80 96 96 112 112 128 128 144 144 (counts climbing from 32 to 144, each twice: a scale falling) |
| 100-103 | $90 | $0090-$009F: 55 34 93 92 201 191 82 78 196 73 78 75 69 89 164 80 (from $0095, the keywords' letters ?RNDINKEY$P) | $C36D: 144 144 128 128 112 112 96 96 80 80 64 64 48 48 32 32 (the same back down: a scale rising) |
| 104-107 | $A0 | $00A0-$00AF: 201 70 206 80 79 73 78 212 83 67 82 69 69 78 164 65 (from $00A0, the keywords' letters IFNPOINTSCREEN$A) | $C37D: 144 128 112 128 112 96 112 96 80 96 80 64 80 64 48 32 (counts in a zigzag from 144 down to 32: a zigzag rising) |
| 108-111 | $B0 | $00B0-$00BF: 84 84 210 65 212 84 65 194 86 65 76 164 67 79 68 197 (from $00B0, the keywords' letters TTRATTABVAL$CODE) | $C38D: 64 64 64 48 80 80 80 80 64 64 64 48 80 80 80 80 (a figure of eight counts between 48 and 80, twice) |
The villain of graphics 96 to 99: BC comes to $80, so its twelve waves a turn are pitched by the ROM's bytes $0080 to $008F, one for each turn of sixteen.
Recorded: the game running, the knight standing where a new game put him: the villain of record $BD8E (graphic 98 when the run began) put in his cell 64 units from him along U at the start of every turn, so that it stayed in sight and out of his reach; seventeen turns, the first silent (it had not been drawn the turn before).
Checked: 192 waves from CLICK (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.38 s, 384 edges, 17 turns, 523 to 32407 Hz.
The villain of graphics 100 to 103: BC comes to $90, so its twelve waves a turn are pitched by the ROM's bytes $0090 to $009F, one for each turn of sixteen.
Recorded: the game running, the knight standing where a new game put him: the villain of record $BD7E (graphic 102 when the run began) put in his cell 64 units from him along U at the start of every turn, so that it stayed in sight and out of his reach; seventeen turns, the first silent (it had not been drawn the turn before).
Checked: 192 waves from CLICK (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.33 s, 384 edges, 17 turns, 659 to 3623 Hz.
The villain of graphics 104 to 107: BC comes to $A0, so its twelve waves a turn are pitched by the ROM's bytes $00A0 to $00AF, one for each turn of sixteen.
Recorded: the game running, the knight standing where a new game put him: the villain of record $BD6E (graphic 106 when the run began) put in his cell 64 units from him along U at the start of every turn, so that it stayed in sight and out of his reach; seventeen turns, the first silent (it had not been drawn the turn before).
Checked: 192 waves from CLICK (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.33 s, 384 edges, 17 turns, 626 to 1975 Hz.
The villain of graphics 108 to 111: BC comes to $B0, so its twelve waves a turn are pitched by the ROM's bytes $00B0 to $00BF, one for each turn of sixteen.
Recorded: the game running, the knight standing where a new game put him: the villain of record $BD5E (graphic 110 when the run began) put in his cell 64 units from him along U at the start of every turn, so that it stayed in sight and out of his reach; seventeen turns, the first silent (it had not been drawn the turn before).
Checked: 192 waves from CLICK (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.34 s, 384 edges, 17 turns, 626 to 1975 Hz.
Recorded by calling BLIP_FROM_TABLE ($C335) sixteen times for each, with BC the table's address (VILLAIN_PITCHES plus 16 for each kind, which is what the two mistakes stop VILLAIN_WANDER from giving it), IX on a record whose drawn flag is set, and the turn counter 0 to 15, a quiet turn apart: the villain in sight for sixteen turns, as the runs above have it. None of this is ever heard in the game.
The second of the five sixteens in BLIP_PITCHES, $C35D: counts climbing from 32 to 144, each twice: a scale falling.
Recorded: sixteen calls of BLIP_FROM_TABLE ($C335) with BC = $C35D, the turn counter 0 to 15, a quiet turn apart.
Checked: 192 waves (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.09 s, 384 edges, 915 to 3829 Hz.
The third of the five sixteens in BLIP_PITCHES, $C36D: the same back down: a scale rising.
Recorded: sixteen calls of BLIP_FROM_TABLE ($C335) with BC = $C36D, the turn counter 0 to 15, a quiet turn apart.
Checked: 192 waves (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.09 s, 384 edges, 915 to 3829 Hz.
The fourth of the five sixteens in BLIP_PITCHES, $C37D: counts in a zigzag from 144 down to 32: a zigzag rising.
Recorded: sixteen calls of BLIP_FROM_TABLE ($C335) with BC = $C37D, the turn counter 0 to 15, a quiet turn apart.
Checked: 192 waves (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.09 s, 384 edges, 915 to 3829 Hz.
The fifth of the five sixteens in BLIP_PITCHES, $C38D: a figure of eight counts between 48 and 80, twice.
Recorded: sixteen calls of BLIP_FROM_TABLE ($C335) with BC = $C38D, the turn counter 0 to 15, a quiet turn apart.
Checked: 192 waves (the code implies 192); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing.
1.06 s, 384 edges, 1619 to 2632 Hz.
When the last villain's sparkles have gone, CHECK_QUEST_DONE goes to GAME_OVER -- the same game-over screen and tune as when the lives run out -- and only then, finding no villain alive, does GAME_OVER set up the ending. Its four villain pictures (ENDING_VILLAIN) come across one at a time and sink into the pit, and every turn one moves, ENDING_BEEP plays twelve waves at 64 plus EFFECT_TIME, counted up one each time -- a slowly falling note -- which is reset as each picture sinks out of sight, so each one's note starts again from the top. When the last has gone, ENDING_PIT_FRONT plays the ending's tune, and it is the menu. The note is Pentagram's pause beep, put to another use (Pentagram's sounds); Nightshade's pause (PAUSE) makes no sound at all.
Recorded: the game running where a new game put him: the four villain records emptied at the start of a turn, which is how CHECK_QUEST_DONE finds them once the fourth villain has been destroyed and its sparkles have gone; recorded from that turn to the start of the new game.
Checked: 2,160 waves from CLICK (the code implies 2,160); every on half-wave was 13B + 18 T-states for the B the code gives it; every off half-wave inside a sound was 13B plus 64 (BEEP's), the loops' own timing; and the tune at $C803: the table's wave counts make 1,188 waves, so 2,375 edges (the first OUT turns off a speaker already off); the recording has 2,375 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply; the tune at $C81A: the table's wave counts make 2,314 waves, so 4,627 edges (the first OUT turns off a speaker already off); the recording has 4,627 -- the same. Every off half-wave was D + 39 T-states and every on half-wave inside a note was D + 62, D being 13B + 3339C - 3333 for the note's B and C, as PLAY_NOTE's loops imply (the edge after a tune's last wave, where the next OUT to the port -- the screen cleared, the border set black -- turned the speaker off, left out).
17.49 s, 11,323 edges, 178 turns, 945 to 1975 Hz.
Each routine and table compared byte for byte, at this build, with the other games' snapshots (their own builds write them), at the place each has it. "Addresses" means the only bytes that differ are the operands naming variables, tables and routines, which each game keeps in its own places. The sounds pages: Pentagram, Alien 8, Knight Lore.
| Here | What | Bytes | Pentagram | Alien 8 | Knight Lore | Note |
|---|---|---|---|---|---|---|
| CLICK | CLICK, one wave | 16 | $D682: identical | $B702: identical | $B4ED: identical | |
| $C46A | BEEP, C waves | 7 | $D67B: 2 bytes differ: addresses | $B6FB: 2 bytes differ: addresses | $B4E6: 2 bytes differ: addresses | |
| PLAY_NOTE | PLAY_NOTE, the tune player's note | 88 | $D6C0: 4 bytes differ: addresses | $B4C5: 4 bytes differ: addresses | $B2DA: 4 bytes differ: addresses | |
| NOTES | NOTES, the note table | 183 | $D718: identical | $B51D: identical | $B332: identical | |
| PLAY_TUNE | PLAY_TUNE, a tune through | 11 | $D6B5: 2 bytes differ: addresses | $B4BA: 2 bytes differ: addresses | $B2CF: 2 bytes differ: addresses | |
| PLAY_TUNE_ONCE | PLAY_TUNE_ONCE, the menu's tune, once | 25 | $D69C: 6 bytes differ: addresses | $B4A1: 6 bytes differ: addresses | $B2B6: 6 bytes differ: addresses | |
| BLIP_BY_TURN | BLIP_BY_TURN, the creature's blip | 27 | $D56C: 6 bytes differ: addresses | Never reached in Pentagram. | ||
| BLIP_PITCHES | BLIP_PITCHES, the five sixteens of pitches | 80 | $D587: identical | |||
| ENDING_BEEP | ENDING_BEEP, the ending's note (Pentagram's pause beep) | 13 | $D5D7: 4 bytes differ: addresses | |||
| $C3AF | BUMP_SOUND, the bump after its test (Pentagram's jump) | 14 | $D5E4: 2 bytes differ: addresses | Nightshade's has five bytes in front, the test of the drawn flag. | ||
| EFFECT_NOTE | EFFECT_NOTE, an effect's note a turn | 27 | $D5F2: 8 bytes differ: addresses | |||
| EFFECT_TABLE | EFFECT_TABLE, the four effects | 28 | $D60D: 8 bytes differ: addresses | The same twenty notes; Pentagram starts effects 0 and 1 only. | ||
| FIRE_SOUND | FIRE_SOUND, a throw (Pentagram's bolt) | 12 | $D629: 2 bytes differ: addresses | |||
| $C40B | FOOTSTEP, the walking half of a footstep | 21 | $D639: 4 bytes differ: a jump, the waves | Four waves where Pentagram's are two; Nightshade's has the count and the test of the speed in front, and the faster half after. | ||
| $C43E | PUFF_SOUND, a puff after its test | 23 | $D64E: 5 bytes differ: addresses, the waves | Sixteen waves where Pentagram's are four; Nightshade's has the test and clearing of the drawn flag in front. | ||
| APPEAR_SOUND | APPEAR_SOUND, a monster appearing | 13 | $D665: identical | Pentagram's bytes are never reached, and are followed by a data byte before its BEEP; Nightshade's jump to BEEP is one shorter. | ||
| ARRIVE_SOUND | ARRIVE_SOUND, the knight appearing | 7 | $D674: 2 bytes differ: addresses | Pentagram's reads a spare byte nothing writes; Nightshade's reads ARRIVING. |