# x86 machine code (.COM file): <del>121</del> <del>120</del> 113 bytes

Hexdump:

    00000000  b4 3e bb 01 00 cd 21 b4  3c 31 c9 ba 40 01 cd 21  |.>....!.<1..@..!|
    00000010  42 b4 09 cd 21 be 5a 01  81 fe 71 01 0f 84 e0 fe  |B...!.Z...q.....|
    00000020  8a 0c 46 e8 07 00 b1 32  e8 02 00 eb eb 88 cb c0  |..F....2........|
    00000030  e3 07 c1 e1 04 30 d2 b4  02 00 da cd 21 e2 fa c3  |.....0......!...|
    00000040  61 2e 73 6e 64 00 00 00  18 ff ff ff ff 00 00 00  |a.snd...........|
    00000050  02 00 00 10 00 00 00 00  01 24 33 33 99 99 99 66  |.........$33...f|
    00000060  99 99 99 99 99 66 33 99  99 99 99 66 33 33 33 33  |.....f3....f3333|
    00000070  33                                                |3|

Can be easily run under DosBox; the output is a .SND file named `a.snd`. [Here is a FLAC version][1] of its output (and [here][2] the .COM file).

Commented assembly:

        org 100h
    
    start:
        ; close stdout
        mov ah,3eh
        mov bx,1
        int 21h
        ; open a.snd
        mov ah,3ch
        xor cx,cx
        mov dx,filename
        int 21h
        ; write the header
        inc dx
        mov ah,9h
        int 21h
        mov si,data
    .l:
        ; data read cycle
        ; quit if we reached the end of data
        cmp si,eof
        je 0
        ; read the current byte in cl (zero-extending to 16-bit)
        ; notice that ch is already zero (at the first iteration it's 0 from the
        ; int 21h/3ch, then we are coming from gen, which leaves cx to zero)
        mov cl,[si]
        ; move to next byte
        inc si
        ; generate the tone
        call gen
        ; generate the pause
        mov cl,50
        call gen
        ; rinse and repeat
        jmp .l
    
    gen:
        ; generate a sawtooth wave at sampling frequency/2 Hz
        ; receives length (in samples>>4) in cx, with lowest bit indicating if
        ; it has to write a wave or a pause
        mov bl,cl
        ; shift the rightmost bit all the way to the left; this kills the
        ; unrelated data and puts a 128 in bl (if cx & 1 != 0)
        shl bl,7
        ; rescale the samples number
        shl cx,4
        ; zero the starting signal
        xor dl,dl
        ; prepare the stuff for int 21h
        mov ah,2h
    .l:
        ; increment the signal
        add dl,bl
        ; write it
        int 21h
        ; decrement iteration count and loop
        loop .l
        ret
    
        ; here be the data
        ; used for the file name; we recycle the .snd from the beginning
        ; of the file header, exploiting the fact that it comes just before a zero
    filename:
        db "a"
    header:
        ; .SND file header (8000 samples, mono, PCM)
        db ".snd",0,0,0,24,0xff,0xff,0xff,0xff,0,0,0,2,0,0,0x10,0,0,0,0,1
        ; terminator for int 21h/ah=9h
        db '$'
    data:
        ; generated by gendata.py
        incbin "data.dat"
    eof:


The `data.dat` included above is an easy-to-use representation of the morse string (lower bit: sound on/sound off, upper 7 bits: sound length in samples >> 4) generated by a Python script:

    #!/usr/bin/env python2
    import sys
    
    # source string
    s = "..--- ----- .---- ....."
    # samples
    sr = 4096
    conv =  {
                '.': 1 | (((sr/5) >> 4) & ~1),    # dot:   1/5 second, dI/dt=1
                '-': 1 | (((sr/5*3) >> 4) & ~1),  # line:  3/5 second, dI/dt=1
                ' ':     ((sr/5*2) >> 4) & ~1     # space: 2/5 second (+1/5 from the always-present pause), dI/dt=0 (silent)
            }
    sys.stdout.write(''.join(chr(conv[a]) for a in s))


  [1]: https://bitbucket.org/mitalia/pcg/downloads/morse.flac
  [2]: https://bitbucket.org/mitalia/pcg/downloads/morse.com