x86 machine code (.COM file): 121 120 113 109 bytes
Hexdump:
00000000 b4 3e bb 01 00 cd 21 b4 3c 31 c9 ba 3e 01 cd 21 |.>....!.<1..>..!|
00000010 4a b4 09 cd 21 be 56 01 8a 0c 46 e8 0c 00 b1 32 |J...!.V...F....2|
00000020 e8 07 00 81 fe 6d 01 75 ef c3 88 cb c0 e3 07 c1 |.....m.u........|
00000030 e1 04 30 d2 b4 02 00 da cd 21 e2 fa c3 2e 73 6e |..0......!....sn|
00000040 64 00 00 00 18 ff ff ff ff 00 00 00 02 00 00 10 |d...............|
00000050 00 00 00 00 01 24 33 33 99 99 99 66 99 99 99 99 |.....$33...f....|
00000060 99 66 33 99 99 99 99 66 33 33 33 33 33 |.f3....f33333|
0000006d
Can be easily run under DosBox; the output is a .SND file named SND
. Here is a FLAC version of its output (and here the .COM file).
Commented assembly:
org 100h
start:
; close stdout
mov ah,3eh
mov bx,1
int 21h
; open snd
mov ah,3ch
xor cx,cx
mov dx,filename
int 21h
; write the header
; we used the `snd` part of the header as file name, back off one byte
dec dx
mov ah,9h
int 21h
mov si,data
.l:
; data read cycle
; 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
; repeat until we reach the end of data
cmp si,eof
jne .l
; quit
ret
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
; .SND file header (4096 samples, mono, PCM)
header:
db "."
; we also use "snd" as the file name
filename:
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))