← D566 Active tune pointer Map D5A5 → Note period/duration table
D568: Play the next note of the active tune
Advances the tune pointer past the next byte of the active tune. Plays that byte as a square-wave note or a silent rest, or returns it when it is the terminator.
Output
A $0D at end-of-tune, otherwise a non-$0D speaker residue
A caller points the tune pointer at a tune and calls this once per byte until the terminator comes back. A tune is a byte stream with three byte classes. $0D ends the tune, $FE is a rest, and any other value is a note.
A note byte is a byte offset into the period/duration table, so it is always even, at twice the note index. A rest borrows the table's first entry for its length and plays it silently.
play_tune_note D568 LD HL,(tune_ptr) Fetch the next tune byte through the tune pointer and advance it by 1.
D56B LD A,(HL)
D56C INC HL
D56D LD (tune_ptr),HL
D570 CP $0D The terminator: return it in A for the caller's end test.
D572 RET Z
D573 CP $FE A rest falls through to the rest path; any other value is a note.
D575 JP NZ,play_tune_note_0
D578 LD HL,$0000 Rest: patch the speaker write to two NOPs to silence it, and select table entry 0 in A for the rest's duration.
D57B LD (tone_out_patch),HL
D57E XOR A
D57F JP play_tune_note_1
play_tune_note_0 D582 LD HL,SPEAKER_OUT_OPCODE Note: patch the speaker write to OUT ($FE),A so the tone sounds.
D585 LD (tone_out_patch),HL
play_tune_note_1 D588 LD HL,note_table Index the period/duration table by the raw byte value.
D58B LD E,A
D58C LD D,$00
D58E ADD HL,DE
D58F LD E,(HL) Read the period, which sets the pitch, into E and the duration in speaker half-cycles into B.
D590 INC HL
D591 LD B,(HL)
D592 LD HL,$0000 Point HL at the start of ROM so the delay loop's store hits ROM harmlessly. Start A at the first speaker level, with the border bits clear for black.
D595 XOR A
Square-wave generator. Each half-cycle flips the speaker and holds it for a delay set by the note's period. The border bits are never touched, so the border stays black for the whole note. The speaker write is patched at run time, so the disassembly from the speaker write onward is mis-aligned with the run-time code. At run time the speaker write takes two bytes, followed by the time-burn store. The disassembly re-syncs with the run-time path at the delay loop below.
play_tune_note_2 D596 PUSH DE Save the period so it can be reloaded each half-cycle.
D597 XOR $10 Flip the speaker bit (ULA bit 4).
tone_out_patch D599 DEFB $D3
D59A CP $36
D59C NOP
D59D DEC E Inner delay: spin E times to set the half-period. A lower E gives a higher pitch.
D59E JR NZ,tone_out_patch+$2
D5A0 POP DE Reload the period for the next half-cycle.
D5A1 NOP
D5A2 DJNZ play_tune_note_2 Repeat for B half-cycles, the note's duration.
D5A4 RET
← D566 Active tune pointer Map D5A5 → Note period/duration table
Fork me on GitHub