# Play a tune

[Courses](https://asm-editor.specy.app/learn/courses.md) › [Z80 assembly](https://asm-editor.specy.app/learn/courses/z80.md) › Examples

Web page: https://asm-editor.specy.app/learn/courses/z80/examples/play-a-tune

> Play the first phrase of Ode to Joy from a table of pitches and durations, waiting for each note to end before starting the next.

The same program in: [MIPS](https://asm-editor.specy.app/learn/courses/mips/examples/play-a-tune.md), [RISC-V](https://asm-editor.specy.app/learn/courses/risc-v/examples/play-a-tune.md), [x86-64](https://asm-editor.specy.app/learn/courses/x86/examples/play-a-tune.md)

This program plays the first phrase of Beethoven's "Ode to Joy". A loop reads a table of pitches
and durations, playing one note and waiting for it to end before moving to the next. Turn your
sound on to hear the tune.

Each note occupies four bytes in the table: a two-byte pitch followed by a two-byte duration
in milliseconds. `.dw` stores each word low byte first. The pitches used here fit in one
byte, so the loop reads the pitch's low byte and skips its high byte. It reads both bytes of the
duration: 200 fits in a byte, but durations such as 400 and 600 exceed 255.

For this table, a pitch of 0 marks the end. The final `.dw 0` has no duration after it, so the loop
checks for zero before loading a duration.

```z80
    .org 0x8000
    ld a, 0
    out (0x62), a   ; instrument 0, the acoustic grand piano
    ld a, 100
    out (0x63), a   ; volume, 0 to 127
    ld hl, tune     ; address of the next note

next:
    ld e, (hl)      ; the pitch, the low byte of its word
    inc hl
    inc hl          ; skip the pitch word's unused high byte
    ld a, e
    or a            ; this table uses pitch 0 as its end marker
    jr z, done
    ld a, (hl)      ; the duration's low byte
    inc hl
    ld b, (hl)      ; and its high byte
    inc hl
    ld c, 0x64
    out (c), a      ; store the duration: high byte in b, low byte in a
    ld b, e         ; duration is stored; reuse b for the pitch
    ld c, 0x61
    in a, (c)       ; play the note at pitch b, then wait for it to end
    jr next

done:
    halt

    .org 0x9000
tune:               ; pitch, milliseconds
    .dw 64, 400,  64, 400,  65, 400,  67, 400
    .dw 67, 400,  65, 400,  64, 400,  62, 400
    .dw 60, 400,  60, 400,  62, 400,  64, 400
    .dw 64, 600,  62, 200,  62, 800
    .dw 0
```

The pitch numbers choose the notes: 60 is middle C, 62 is D, 64 is E, 65 is F and 67 is G. Most
notes last 400 milliseconds. The final three notes last 600, 200 and 800 milliseconds.

To set the duration, the loop loads its low byte into `a` and its high byte into `b`, then writes
to port `0x64`. With `out (c), a`, `b` supplies the high byte of the port address; this port uses
that byte as the duration's high byte and `a` as its low byte. The device remembers the duration
after the write, so `ld b, e` can reuse `b` for the pitch.

With `c` set to `0x61`, `in a, (c)` sends that pitch to the simulator's play-and-wait port. It starts
the note, waits for its duration, then returns 0 in `a`. The loop does not need that return value.
The instrument, volume and duration settings remain in effect until changed. Instrument and
volume are the same for every note here, so they are written once before the loop.

The whole phrase takes 6.4 seconds, the sum of the durations. In a testcase, no sound is played:
each wait completes immediately and advances the virtual clock by the note's duration.

## Try it

Change the first `ld a, 0` to `ld a, 73` and run the program again to hear the same tune on a flute.
You can also use 40 for a violin or 56 for a trumpet.

Then replace `ld c, 0x61` followed by `in a, (c)` with `ld a, e` followed by `out (0x60), a`.
Port `0x60` starts each note without waiting for its duration. If the instrument sounds have not
loaded, the program pauses at the first write until the browser finishes loading them. Once they
are ready, all fifteen notes start within a few instructions of each other and overlap. They can
keep sounding after the program reaches `halt`.

## Instructions in this Lecture

- Z80: [`ld`](https://asm-editor.specy.app/documentation/z80/instruction/ld.md), [`out`](https://asm-editor.specy.app/documentation/z80/instruction/out.md), [`inc`](https://asm-editor.specy.app/documentation/z80/instruction/inc.md), [`or`](https://asm-editor.specy.app/documentation/z80/instruction/or.md), [`jr`](https://asm-editor.specy.app/documentation/z80/instruction/jr.md), [`in`](https://asm-editor.specy.app/documentation/z80/instruction/in.md), [`halt`](https://asm-editor.specy.app/documentation/z80/instruction/halt.md)

Previous: [Flappy ASM](https://asm-editor.specy.app/learn/courses/z80/examples/flappy-asm.md)

Next: [Chiptune](https://asm-editor.specy.app/learn/courses/z80/examples/chiptune.md)
